#include "stdafx.h"
#include "ShellPidl.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif


///////////////////////////////////////////////////////////////////////
//
// This source is part of CShellTree - Selom Ofori
// 
// Version: 1.02 (any previously unversioned copies are older/inferior
//
// This code is free for all to use. Mutatilate it as much as you want
// See MFCENUM sample from microsoft

// 
// FUNCTIONS THAT DEAL WITH PIDLs
//
/****************************************************************************
*
*    FUNCTION: Next(LPCITEMIDLIST pidl)
*
*    PURPOSE:  Gets the next PIDL in the list 
*
****************************************************************************/
LPITEMIDLIST CShellPidl::Next(LPCITEMIDLIST pidl)
{
   LPSTR lpMem=(LPSTR)pidl;

   lpMem+=pidl->mkid.cb;

   return (LPITEMIDLIST)lpMem;
}

/****************************************************************************
*
*    FUNCTION: GetSize(LPCITEMIDLIST pidl)
*
*    PURPOSE:  Gets the size of the PIDL 
*
****************************************************************************/
UINT CShellPidl::GetSize(LPCITEMIDLIST pidl)
{
    UINT cbTotal = 0;
    if (pidl)
    {
        cbTotal += sizeof(pidl->mkid.cb);       // Null terminator
        while (pidl->mkid.cb)
        {
            cbTotal += pidl->mkid.cb;
            pidl = Next(pidl);
        }
    }

    return cbTotal;
}

/****************************************************************************
*
*    FUNCTION: CreatePidl(UINT cbSize)
*
*    PURPOSE:  Allocates a PIDL 
*
****************************************************************************/
LPITEMIDLIST CShellPidl::CreatePidl(UINT cbSize)
{
    LPMALLOC lpMalloc;
    HRESULT  hr;
    LPITEMIDLIST pidl=NULL;

    hr=SHGetMalloc(&lpMalloc);

    if (FAILED(hr))
       return 0;

    pidl=(LPITEMIDLIST)lpMalloc->Alloc(cbSize);

    if (pidl)
        memset(pidl, 0, cbSize);      // zero-init for external task   alloc

    if (lpMalloc) lpMalloc->Release();

    return pidl;
}

/****************************************************************************
*
*    FUNCTION: ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
*
*    PURPOSE:  Concatenates two PIDLs 
*
****************************************************************************/
LPITEMIDLIST CShellPidl::ConcatPidls(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    LPITEMIDLIST pidlNew;
    UINT cb1;
    UINT cb2;

    if (pidl1)  //May be NULL
       cb1 = GetSize(pidl1) - sizeof(pidl1->mkid.cb);
    else
       cb1 = 0;

    cb2 = GetSize(pidl2);

    pidlNew = CreatePidl(cb1 + cb2);
    if (pidlNew)
    {
        if (pidl1)
           memcpy(pidlNew, pidl1, cb1);
        memcpy(((LPSTR)pidlNew) + cb1, pidl2, cb2);
    }
    return pidlNew;
}

/****************************************************************************
*
*    FUNCTION: CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi)
*
*    PURPOSE:  Copies the ITEMID 
*
****************************************************************************/
LPITEMIDLIST CShellPidl::CopyITEMID(LPMALLOC lpMalloc, LPITEMIDLIST lpi)
{
   LPITEMIDLIST lpiTemp;

   lpiTemp=(LPITEMIDLIST)lpMalloc->Alloc(lpi->mkid.cb+sizeof(lpi->mkid.cb));
   CopyMemory((PVOID)lpiTemp, (CONST VOID *)lpi, lpi->mkid.cb+sizeof(lpi->mkid.cb));

   return lpiTemp;
}

/****************************************************************************
*
*    FUNCTION: GetName(LPSHELLFOLDER lpsf,LPITEMIDLIST  lpi,DWORD dwFlags,
*             LPSTR         lpFriendlyName)
*
*    PURPOSE:  Gets the friendly name for the folder 
*
****************************************************************************/
BOOL CShellPidl::GetName(LPSHELLFOLDER lpsf,
             LPITEMIDLIST  lpi,
			 DWORD         dwFlags,
             LPTSTR         lpFriendlyName,int nMaxLen)
{
	BOOL   bSuccess=TRUE;
	STRRET str;

	//for how to use the STRRET structure, please look at 
	//http://blogs.msdn.com/oldnewthing/archive/2004/08/23/218837.aspx
	//
	//modified by bfchen 
	if (NOERROR==lpsf->GetDisplayNameOf(lpi,dwFlags, &str))
	{
		if(SUCCEEDED(StrRetToBuf(&str,lpi,lpFriendlyName,nMaxLen)))
		{
			return TRUE;
		}
	}
	return FALSE;
//
//   if (NOERROR==lpsf->GetDisplayNameOf(lpi,dwFlags, &str))
//   {
//      switch (str.uType)
//      {
//         case STRRET_WSTR:
//#ifdef _UNICODE 
//			wcscpy(lpFriendlyName,str.pOleStr);
//#else
//            WideCharToMultiByte(CP_ACP,                 // CodePage
//                                0,		               // dwFlags
//                                str.pOleStr,            // lpWideCharStr
//                                -1,                     // cchWideChar
//                                lpFriendlyName,         // lpMultiByteStr
//								MAX_PATH,
//								NULL,
//								NULL
//								);
//#endif
//             break;
//
//         case STRRET_OFFSET:
//
//             _tcscpy(lpFriendlyName, (LPCTSTR)lpi+str.uOffset);
//             break;
//
//         case STRRET_CSTR:
//#ifdef _UNICODE 
//			WideCharToMultiByte(CP_ACP,                // CodePage
//                                0,		               // dwFlags
//                                str.cStr,            // lpWideCharStr
//                                -1,                     // cchWideChar
//                                lpFriendlyName,         // lpMultiByteStr
//								MAX_PATH,
//                                //sizeof(lpFriendlyName), // cchMultiByte, wrong. sizeof on a pointer, psk, psk
//                                NULL,                   // lpDefaultChar,
//                                NULL);                  // lpUsedDefaultChar
//
//#else
//             _tcscpy(lpFriendlyName, str.cStr);
//#endif
//             break;
//
//         default:
//             bSuccess = FALSE;
//             break;
//      }
//   }
//   else
////      bSuccess = FALSE;
//
//   return bSuccess;
}

/****************************************************************************
*
*  FUNCTION: DoTheMenuThing(HWND hwnd, 
*                           LPSHELLFOLDER lpsfParent,
*                           LPITEMIDLIST  lpi,
*                           LPPOINT lppt)
*
*  PURPOSE: Displays a popup context menu, given a parent shell folder,
*           relative item id and screen location.
*
*  PARAMETERS:
*    hwnd       - Parent window handle
*    lpsfParent - Pointer to parent shell folder.
*    lpi        - Pointer to item id that is relative to lpsfParent
*    lppt       - Screen location of where to popup the menu.
*
*  RETURN VALUE:
*    Returns TRUE on success, FALSE on failure
*
****************************************************************************/
BOOL CShellPidl::DoTheMenuThing(HWND hwnd, LPSHELLFOLDER lpsfParent,
     LPITEMIDLIST  lpi, LPPOINT lppt)
{
    LPCONTEXTMENU lpcm;
    HRESULT       hr;
    TCHAR          szTemp[64];
    //CMINVOKECOMMANDINFO cmi;
	CMINVOKECOMMANDINFOEX cmi; //modified by bfchen
    DWORD               dwAttribs=0;
    int                 idCmd;
    HMENU               hMenu;
    BOOL                bSuccess=TRUE;

    hr=lpsfParent->GetUIObjectOf(hwnd,
        1,  //Number of objects to get attributes of
        (const struct _ITEMIDLIST **)&lpi,
        IID_IContextMenu,
        0,
        (LPVOID *)&lpcm);
    if (SUCCEEDED(hr))  
    {
       hMenu = CreatePopupMenu();

       if (hMenu)
       {
          hr=lpcm->QueryContextMenu(hMenu, 0, 1, 0x7fff, CMF_EXPLORE);
          if (SUCCEEDED(hr))
          {
             idCmd=TrackPopupMenu(hMenu, 
                TPM_LEFTALIGN | TPM_RETURNCMD | TPM_RIGHTBUTTON, 
                lppt->x, lppt->y, 0, hwnd, NULL);

             if (idCmd)
             {
                cmi.hwnd   = hwnd;
#ifdef _UNICODE
				cmi.cbSize = sizeof(CMINVOKECOMMANDINFOEX);
				cmi.fMask = CMIC_MASK_UNICODE;
                cmi.lpVerbW = MAKEINTRESOURCE(idCmd-1);
#else
				cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
				cmi.fMask  = 0;
				cmi.lpVerb = MAKEINTRESOURCE(idCmd-1);
#endif
                cmi.lpParameters = NULL;
      	        cmi.lpDirectory  = NULL;
                cmi.nShow        = SW_SHOWNORMAL;
                cmi.dwHotKey     = 0;
                cmi.hIcon        = NULL;
				//just cat the CMINVOKECOMMANDINFOEX* to CMINVOKECOMMANDINFO*
				//it is safe
                hr=lpcm->InvokeCommand((CMINVOKECOMMANDINFO*)&cmi);
                if (!SUCCEEDED(hr))  
                {
                   _stprintf(szTemp, _T("InvokeCommand failed. hr=%lx"), hr);
                }
             }

          }
          else
             bSuccess = FALSE;

          DestroyMenu(hMenu);
       }
       else
          bSuccess = FALSE;

       lpcm->Release();
    } 
    else
    {
       _stprintf(szTemp,_T("GetUIObjectOf failed! hr=%lx"), hr);
       bSuccess = FALSE;
    }
    return bSuccess;
}

/****************************************************************************
*
*    FUNCTION: GetIcon(LPITEMIDLIST lpi, UINT uFlags)
*
*    PURPOSE:  Gets the index for the current icon.  Index is index into system
*              image list.
*
*  PARAMETERS:
*    lpi    - Fully qualified item id list for current item.
*    uFlags - Flags for SHGetFileInfo()
*
*  RETURN VALUE:
*    Icon index for current item.
****************************************************************************/
int CShellPidl::GetItemIcon(LPITEMIDLIST lpi, UINT uFlags)
{
   SHFILEINFO    sfi;

   SHGetFileInfo((LPCTSTR)lpi, 
                 0,
                 &sfi, 
                 sizeof(SHFILEINFO), 
                 uFlags);

   return sfi.iIcon;
}
